/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */


#include <core/schedule.h>
#include <caps/addrspace.h>
#include <caps/cnode.h>
#include <caps/endpoint.h>
#include <caps/notification.h>
#include <caps/task.h>
#include <uapi/errors.h>
#include <uapi/ipcops.h>

static inline void cap_cnode_set_obj(capability_t *cap, capability_t *obj)
{
    assert(((unsigned long)obj & lowbitsmask(CAP_TYPE_BITS)) == 0);
    cap_set_short_info(cap, (unsigned long)obj >> CAP_TYPE_BITS);
}

static inline void cap_cnode_set_detail(capability_t *cap, unsigned int guardlen, unsigned int guard,
                                        unsigned int radix)
{
    unsigned long info = 0;

    assert(guardlen >= 1 && guardlen <= GUARDBITS && guardlen < bitmask(GUARDBITS));
    assert(guard < bitmask(guardlen));
    assert(radix >= RADIXBASE && radix - RADIXBASE < bitmask(RADIXBITS));

    radix -= RADIXBASE;

    info |= radix << RADIXOFFSET;
    info |= guardlen << GUARDLENOFFSET;
    info |= guard << GUARDOFFSET;

    cap_set_long_info(cap, info);
}

capability_t *cap_cnode_get_audit(capability_t *cnode)
{
    return cap_cnode_get_obj(cnode) + CAP_INDEX_AUDIT;
}

void cap_cnode_recycle(capability_t *cnode)
{
    capability_t *caps = cap_cnode_get_obj(cnode);
    unsigned int  size = cap_cnode_get_radix(cnode);
    unsigned int  type;

    while (size > 0) {
        type = cap_get_type(caps);
        switch (type) {
            case CAP_CNODE:
                cap_cnode_put(caps);
                break;

            case CAP_ADDRSPACE:
                cap_addrspace_put(caps);
                break;

            case CAP_EP:
                cap_ep_put(caps);
                break;

            case CAP_NTFN:
                (void)cap_ntfn_put(caps);
                break;

            case CAP_THREAD:
                cap_task_put(caps);
                break;

            default:
                assert(type < CAP_COUNT && capobj_size[type] == 0);
                break;
        }

        caps++;
        size--;
    }

    (void)allocator_free((unsigned long)cap_cnode_get_obj(cnode), size * sizeof(capability_t));
    cap_make_empty(cnode);
}

void cap_cnode_init(capability_t *cap, capability_t *cnode, unsigned int guardlen, unsigned int guard,
                    unsigned int radix)
{
    assert(guard <= lowbitsmask(guardlen));
    assert(guardlen + radix <= sizeof(unsigned long) * 8);

    cap_basic_init(cap, CAP_CNODE);
    cap_cnode_set_obj(cap, cnode);
    cap_cnode_set_detail(cap, guardlen, guard, radix);
    (void)cap_cnode_get(cap);
}

capability_t *cnode_cptr_lookup(capability_t *cnode, unsigned long cptr, unsigned int bits)
{
    capability_t *ret = NULL;
    unsigned int  guard;
    unsigned int  index;
    unsigned int  guardlen = cap_cnode_get_guardlen(cnode);
    unsigned int  radix    = cap_cnode_get_radix(cnode);

    if ((cnode == NULL) || (cap_get_type(cnode) != CAP_CNODE)) {
        return NULL;
    }

    if ((guardlen + radix) <= bits) {
        bits -= guardlen;
        guard = (cptr >> bits) & lowbitsmask(guardlen);

        bits -= radix;
        index = (cptr >> bits) & lowbitsmask(radix);

        if (guard == cap_cnode_get_guard(cnode)) {
            ret = cap_cnode_get_obj(cnode) + index;

            if ((cptr & lowbitsmask(bits)) != 0) {
                if (cap_get_type(ret) != CAP_CNODE) {
                    ret = NULL;
                } else {
                    ret = cnode_cptr_lookup(ret, cptr, bits);
                }
            }
        }
    }

    return ret;
}

void cap_cnode_dispatcher(capability_t *cap, unsigned long op, unsigned long m0, unsigned long m1, unsigned long m2,
                          unsigned long m3, unsigned long m4, unsigned long m5)
{
    task_set_retcode(current(), ERR_TO_RETCODE(-ENOSYS));
}
